`timescale 1ns / 1ps
//////////////////////////////////////////////////////////////////////////////////
// Company: 
// Engineer: 
// 
// Create Date:    12:53:19 11/30/2011 
// Design Name: 
// Module Name:    bridge 
// Project Name: 
// Target Devices: 
// Tool versions: 
// Description: 
//
// Dependencies: 
//
// Revision: 
// Revision 0.01 - File Created
// Additional Comments: 
//
//////////////////////////////////////////////////////////////////////////////////
module ppb (
  input               CLK,
  input               RST,
  
  input               s_pri_tvalid,
  output              s_pri_tready,
  input   [127:0]     s_pri_tdata,
  input   [3:0]       s_pri_tstrb,
  input               s_pri_tlast,
  output              m_pri_tvalid,
  input               m_pri_tready,
  output  [127:0]     m_pri_tdata,
  output  [3:0]       m_pri_tstrb,
  output              m_pri_tlast,
  
  input               s_sec_tvalid,
  output              s_sec_tready,
  input   [127:0]     s_sec_tdata,
  input   [3:0]       s_sec_tstrb,
  input               s_sec_tlast,
  output              m_sec_tvalid,
  input               m_sec_tready,
  output  [127:0]     m_sec_tdata,
  output  [3:0]       m_sec_tstrb,
  output              m_sec_tlast,
  
  output  [31:0]      CfgDo,
  input   [31:0]      CfgDi,
  output  [3:0]       CfgBe,
  output  [9:0]       CfgAddr,
  output              CfgWe,
  output              CfgReq,
  input               CfgAck,

  output reg [31:12] io_base,
  output reg [31:12] io_limit,
  output reg [31:20] mem_base,
  output reg [31:20] mem_limit,
  output reg [63:20] pmem_base,
  output reg [63:20] pmem_limit,
  output reg [7:0]   primary_bus,
  output reg [7:0]   secondary_bus,
  output reg [7:0]   subordinate_bus
  );

  reg         tlp_cfg0;
  reg         tlp_cfg1;
  wire        tlp_ep            = s_pri_tdata[14];
  wire        tlp_td            = s_pri_tdata[15];
  wire        tlp_th            = s_pri_tdata[16];
  wire [4:0]  tlp_type          = s_pri_tdata[28:24];
  wire [2:0]  tlp_fmt           = s_pri_tdata[31:29];
  wire [3:0]  tlp_fdwbe         = s_pri_tdata[35:32];
  wire [7:0]  tlp_tag           = s_pri_tdata[47:40];
  wire [15:0] tlp_requester_id  = s_pri_tdata[63:48];
  wire [9:0]  tlp_reg_num       = s_pri_tdata[75:66];
  wire [2:0]  tlp_function_num  = s_pri_tdata[82:80];
  wire [4:0]  tlp_device_num    = s_pri_tdata[87:83];
  wire [7:0]  tlp_bus_num       = s_pri_tdata[95:88];
  wire [31:0] tlp_data          = s_pri_tdata[127:96];
  
  reg  [2:0]  rTlpFmt;
  reg  [4:0]  rTlpType;
  reg         rTlpTH;
  reg         rTlpTD;
  reg         rTlpEP;
  reg  [3:0]  rTlpFdwbe;
  reg  [7:0]  rTlpTag;
  reg  [15:0] rTlpRequesterId;
  reg  [9:0]  rTlpRegNum;
  reg  [2:0]  rTlpFunctionNum;
  reg  [4:0]  rTlpDeviceNum;
  reg  [7:0]  rTlpBusNum;
  reg  [31:0] rTlpData;
  reg         rCfgType;

  wire          cpl_tvalid;
  wire          cpl_tready;
  wire [127:0]  cpl_tdata;
  wire [3:0]    cpl_tstrb;
  wire [127:0]  cfg_tdata;
  wire [3:0]    cfg_tstrb;
  reg [31:0]    rRdData;
  wire          SecondaryBusMatch;
  wire          MyCfgAck;
  wire          CompletionTimeOut;
  reg [31:0]    CompletionTimer;
  
  localparam 
    CHECK_HEADER = 0,
    ACCEPT_CFG = 1,
    CFG_REQ = 2,
    SEND_CPL = 4,
    SEND_CFG = 5,
    PASS_TLP = 6,
    CFG0_CPL_WAIT = 7,
    SEND_CPL_UR = 8;

  reg [3:0] rState, sState;
  always @(posedge CLK)
  begin
    if (RST)
      rState <= CHECK_HEADER;
    else
      rState <= sState;
  end
  
  always @*
  begin
    sState <= rState;
    case (rState)
      CHECK_HEADER:
        if (s_pri_tvalid)
        begin
          if (tlp_cfg0 || tlp_cfg1)
            sState <= ACCEPT_CFG;
          else
            sState <= PASS_TLP;
        end
      ACCEPT_CFG:
        if (tlp_cfg1)
          sState <= SEND_CFG;
        else
          sState <= CFG_REQ;
      CFG_REQ:
        if (MyCfgAck)
        begin
          if (rCfgType)
            sState <= SEND_CFG;
          else
            sState <= SEND_CPL;
        end
      SEND_CPL:
        if (cpl_tready)
          sState <= CHECK_HEADER;
      SEND_CFG:
        if (m_sec_tvalid && m_sec_tready && m_sec_tlast)
        begin
          if (SecondaryBusMatch)
            sState <= CFG0_CPL_WAIT;
          else
            sState <= CHECK_HEADER;
        end
      PASS_TLP:
        if (s_pri_tvalid && s_pri_tready && s_pri_tlast)
          sState <= CHECK_HEADER;
      CFG0_CPL_WAIT:
        if (CompletionTimeOut)
          sState <= SEND_CPL_UR;
        else if (s_sec_tvalid && s_sec_tready && (s_sec_tdata[28:24] == 5'b01010))
          sState <= CHECK_HEADER;
      SEND_CPL_UR:
        if(cpl_tready)
          sState <= CHECK_HEADER;
    endcase
  end

  always @*
  begin
    tlp_cfg0 <= 1'b0;
    tlp_cfg1 <= 1'b0;
    casex({tlp_fmt, tlp_type})
      8'b0x000100: tlp_cfg0 <= 1'b1;
      8'b0x000101: tlp_cfg1 <= 1'b1;
    endcase
  end

  always @(posedge CLK)
  begin
    if (rState == CHECK_HEADER)
    begin
      rTlpFmt         <= tlp_fmt;
      rTlpType        <= tlp_type;
      rTlpTH          <= tlp_th;
      rTlpTD          <= tlp_td;
      rTlpEP          <= tlp_ep;
      rTlpFdwbe       <= tlp_fdwbe;
      rTlpTag         <= tlp_tag;
      rTlpRequesterId <= tlp_requester_id;
      rTlpRegNum      <= tlp_reg_num;
      rTlpFunctionNum <= tlp_function_num;
      rTlpDeviceNum   <= tlp_device_num;
      rTlpBusNum      <= tlp_bus_num;
      rTlpData        <= tlp_data;
      rCfgType        <= tlp_type[0];
    end
  end
  
  always @(posedge CLK)
  begin
    if (MyCfgAck) 
      rRdData <= CfgDi;
  end

  assign cpl_tvalid         = (rState ==  SEND_CPL) || (rState == SEND_CPL_UR);
  
  assign cpl_tdata[9:0]     = (rState == SEND_CPL_UR) ? 10'h0 :
                              rTlpFmt[1] ? 10'h0 : 10'h1;   //Length
  assign cpl_tdata[11:10]   = 2'b0;                    //AT bits
  assign cpl_tdata[13:12]   = 2'b0;                    //Attr bits
  assign cpl_tdata[14]      = 1'b0;                    //EP bit
  assign cpl_tdata[15]      = 1'b0;                    //TD bit
  assign cpl_tdata[16]      = 1'b0;                    //TH bit
  assign cpl_tdata[17]      = 1'b0;                    //Reserved
  assign cpl_tdata[18]      = 1'b0;                    //Attr 
  assign cpl_tdata[19]      = 1'b0;                    //Reserved
  assign cpl_tdata[22:20]   = 3'b0;                    //TC
  assign cpl_tdata[23]      = 1'b0;                    //Reserved
  assign cpl_tdata[28:24]   = 5'b01010;                //Type
  assign cpl_tdata[31:29]   = (rState == SEND_CPL_UR) ? 3'b000 : 
                              rTlpFmt[1] ? 3'b000 : 3'b010; //Fmt
  assign cpl_tdata[43:32]   = 4;                       //Byte Count
  assign cpl_tdata[44]      = 1'b0;                    //BCM
  assign cpl_tdata[47:45]   = (rState == SEND_CPL_UR) ? 3'b001 : 
                              3'b000;                    //CplStatus
  assign cpl_tdata[50:48]   = rTlpFunctionNum;
  assign cpl_tdata[55:51]   = rTlpDeviceNum;
  assign cpl_tdata[63:56]   = rTlpBusNum;
  assign cpl_tdata[70:64]   = 0;                       //Lower Address
  assign cpl_tdata[71]      = 1'b0;                    //Reserved
  assign cpl_tdata[79:72]   = rTlpTag;                 //Tag
  assign cpl_tdata[95:80]   = rTlpRequesterId;         //Requester ID
  assign cpl_tdata[103:96]  = rRdData[31:24];
  assign cpl_tdata[111:104] = rRdData[23:16];
  assign cpl_tdata[119:112] = rRdData[15:8];
  assign cpl_tdata[127:120] = rRdData[7:0];

  assign cpl_tstrb          = (rState == SEND_CPL_UR) ? 4'h7 :
                              rTlpFmt[1] ? 4'h7 : 4'hF;
  
  assign cfg_tdata[9:0]     = 10'h1;                    //Length
  assign cfg_tdata[11:10]   = 2'b0;                     //AT bits
  assign cfg_tdata[13:12]   = 2'b0;                     //Attr bits
  assign cfg_tdata[14]      = rTlpEP;                   //EP bit
  assign cfg_tdata[15]      = rTlpTD;                   //TD bit
  assign cfg_tdata[16]      = rTlpTH;                   //TH bit
  assign cfg_tdata[17]      = 1'b0;                     //Reserved
  assign cfg_tdata[18]      = 1'b0;                     //Attr 
  assign cfg_tdata[19]      = 1'b0;                     //Reserved
  assign cfg_tdata[22:20]   = 3'b0;                     //TC
  assign cfg_tdata[23]      = 1'b0;                     //Reserved
  assign cfg_tdata[28:24]   = (SecondaryBusMatch) ? {rTlpType[4:1], 1'b0} : rTlpType;   //Type convert it if secondary bus matches
  assign cfg_tdata[31:29]   = rTlpFmt;                  //Fmt
  assign cfg_tdata[35:32]   = rTlpFdwbe;                //First DW BE
  assign cfg_tdata[39:36]   = 4'b0;                     //Last DW BE
  assign cfg_tdata[47:40]   = rTlpTag;                  //Tag
  assign cfg_tdata[63:48]   = rTlpRequesterId;          //Requester ID
  assign cfg_tdata[65:64]   = 2'b0;                     //Reserved
  assign cfg_tdata[75:66]   = rTlpRegNum;               //Register Number
  assign cfg_tdata[79:76]   = 4'b0;                     //Reserved
  assign cfg_tdata[82:80]   = rTlpFunctionNum;          //Function Number
  assign cfg_tdata[87:83]   = rTlpDeviceNum;            //Device Number
  assign cfg_tdata[95:88]   = rTlpBusNum;               //Bus Number
  assign cfg_tdata[127:96]  = rTlpData;                 //Data
  
  assign cfg_tstrb = rTlpFmt[1] ? 4'hF : 4'h7;
  assign SecondaryBusMatch = (secondary_bus == rTlpBusNum);
  
  assign s_pri_tready = ((rState == PASS_TLP) && m_sec_tready) || (rState == ACCEPT_CFG);
  assign m_sec_tvalid = ((rState == PASS_TLP) && s_pri_tvalid) || (rState == SEND_CFG);
  assign m_sec_tdata  = (rState == PASS_TLP) ? s_pri_tdata : cfg_tdata;
  assign m_sec_tstrb  = (rState == PASS_TLP) ? s_pri_tstrb : cfg_tstrb;
  assign m_sec_tlast  = (rState == PASS_TLP) ? s_pri_tlast : 1'b1;


  reg rCfgAccessFlag, sCfgAccessFlag;
  reg [9:0] rCfgLookupAddr, sCfgLookupAddr;
  always @(posedge CLK)
  begin
    if (RST)
    begin
      rCfgAccessFlag <= 1'b0;
      rCfgLookupAddr <= 10'h6;
    end
    else
    begin
      rCfgAccessFlag <= sCfgAccessFlag;
      rCfgLookupAddr <= sCfgLookupAddr;
    end
  end
  
  always @*
  begin
    sCfgAccessFlag <= rCfgAccessFlag;
    sCfgLookupAddr <= rCfgLookupAddr;
    if (CfgAck)
    begin
      if ((rState == CFG_REQ) && !rCfgAccessFlag)
        sCfgAccessFlag <= 1'b1;
      else
        sCfgAccessFlag <= 1'b0;
        
      if (!rCfgAccessFlag)
      begin
        if (rCfgLookupAddr == 10'h0C)
          sCfgLookupAddr <= 10'h06;
        else
          sCfgLookupAddr <= rCfgLookupAddr + 1;
      end
    end
  end
  
  assign CfgDo = {rTlpData[7:0], rTlpData[15:8], rTlpData[23:16], rTlpData[31:24]};
  assign CfgBe = rTlpFdwbe;
  assign CfgAddr = rCfgAccessFlag ? rTlpRegNum : rCfgLookupAddr;
  assign CfgWe = rCfgAccessFlag ? rTlpFmt[1] : 1'b0;
  assign CfgReq = 1'b1;//(rState == CFG_REQ);
  assign MyCfgAck = CfgAck && rCfgAccessFlag;

  always @(posedge CLK)
  begin
    if (CfgAck && !rCfgAccessFlag)
    begin
      case (rCfgLookupAddr)
        10'h06:
        begin
          primary_bus <= CfgDi[7:0];
          secondary_bus <= CfgDi[15:8];
          subordinate_bus <= CfgDi[23:16];
        end
        10'h07:
        begin
          io_base[15:12] <= CfgDi[7:4];
          io_limit[15:12] <= CfgDi[15:12];
        end
        10'h08:
        begin
          mem_base[31:20] <= CfgDi[15:4];
          mem_limit[31:20] <= CfgDi[31:20];
        end
        10'h09:
        begin
          pmem_base[31:20] <= CfgDi[15:4];
          pmem_limit[31:20] <= CfgDi[31:20];
        end
        10'h0A:
          pmem_base[63:32] <= CfgDi[31:0];
        10'h0B:
          pmem_limit[63:32] <= CfgDi[31:0];
        10'h0C:
        begin
          io_base[31:16] <= CfgDi[15:0];
          io_limit[31:16] <= CfgDi[31:16];
        end
      endcase
    end
  end
  
  always @(posedge CLK)
  begin
    if (rState == SEND_CFG)
      CompletionTimer <= 0;
    else
      CompletionTimer <= CompletionTimer + 1;
  end
  
  assign CompletionTimeOut = CompletionTimer[4];
  
  axis_mux #(
    .DWIDTH     (128),
    .KWIDTH     (4))
  m_mux(
    .clk        (CLK        ),
    .rst        (RST        ),
                 
    .s0_tvalid  (cpl_tvalid ),
    .s0_tready  (cpl_tready ),
    .s0_tdata   (cpl_tdata  ),
    .s0_tkeep   (cpl_tstrb  ),
    .s0_tlast   (1'b1),
                 
    .s1_tvalid  (s_sec_tvalid  ),
    .s1_tready  (s_sec_tready  ),
    .s1_tdata   (s_sec_tdata   ),
    .s1_tkeep   (s_sec_tstrb   ),
    .s1_tlast   (s_sec_tlast   ),
                 
    .m_tvalid   (m_pri_tvalid   ),
    .m_tready   (m_pri_tready   ),
    .m_tdata    (m_pri_tdata    ),
    .m_tkeep    (m_pri_tstrb    ),
    .m_tlast    (m_pri_tlast    )
  );
 
endmodule
